import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;

import nanoxml.XMLElement;
import nanoxml.XMLParseException;


public class Parser {
	HashMap<String, Proceso> procesos;
	LinkedList<Instruccion> instrucciones;
	int tam_ram;
	
	public Parser(){
		procesos = new HashMap<String, Proceso>();
		instrucciones = new LinkedList<Instruccion>();
	}
	
	public HashMap<String, Proceso> getProcesos() {
		return procesos;
	}

	public LinkedList<Instruccion> getInstrucciones() {
		return instrucciones;
	}

	public int getTam_ram() {
		return tam_ram;
	}
	
	public int parseArchivo(String ruta){
		XMLElement xml = new XMLElement();
		FileReader reader = null;

		//Se lee el archivo de suplidores
		try {
			reader = new FileReader(ruta);
			xml.parseFromReader(reader);
			inicializar(xml);
			return 0;
		} catch (FileNotFoundException e){
			System.err.println("El archivo XML de entrada no se pudo abrir.");
			return -1;
		} catch (IOException e){
			System.err.println("Error haciendo parsing de "+ruta+", verifique la sintaxis.");
			return -1;
		} catch (InvalidSyntaxException e){
			System.err.println(e.getMessage());
			return -1;
		} catch (XMLParseException e) {
			System.err.println("El archivo XML de entrada esta mal formado.");
			return -1;
		}
	}

	public void inicializar(XMLElement tree) throws FileNotFoundException,
	IOException,
	InvalidSyntaxException,
	XMLParseException
	{
		Enumeration<XMLElement> e = tree.enumerateChildren();
		XMLElement child = null;
		XMLElement nieto = null;

		if(!tree.getName().toLowerCase().equals("simulador")){
			throw new InvalidSyntaxException("Debe usar la etiqueta <simulador> en"
					+ " vez de <"+tree.getName()+">.");
		}

		if(e.hasMoreElements()){
			child = (XMLElement) e.nextElement();
			if(!child.getName().toLowerCase().equals("tam_ram")){
				throw new InvalidSyntaxException("Debe usar la etiqueta <tam_ram> en"
						+ " vez de <"+child.getName()+">.");
			} else {
				tam_ram = Integer.valueOf(child.getContent());
			}
		} else {
			throw new InvalidSyntaxException("Debe usar la etiqueta <tam_ram> "
					+ "para indicar el numero de marcos de pagina que desea.");
		}

		while(e.hasMoreElements()){
			child = (XMLElement) e.nextElement();
			Enumeration<XMLElement> f = child.enumerateChildren();

			if(child.getName().toLowerCase().equals("procesos")){
				while(f.hasMoreElements()){
					nieto = (XMLElement) f.nextElement();
					if(nieto.getName().toLowerCase().equals("proceso")){
						Proceso p = parseProceso(nieto);
						//guardas en estructura global que contiene todos los procesos
						procesos.put(p.getNombre(), p);
					} else {
						throw new InvalidSyntaxException("Debe usar la etiqueta <proceso> en"
								+ " vez de <"+child.getName()+">.");
					}
				}
			} else if (child.getName().toLowerCase().equals("instrucciones")){
				while(f.hasMoreElements()){
					nieto = (XMLElement) f.nextElement();
					if(nieto.getName().toLowerCase().equals("instruccion")){
						Instruccion ins = parseInstruccion(nieto);
						//guardas la instruccion en una lista de instrucciones por ejecutar
						instrucciones.addLast(ins);
					} else {
						throw new InvalidSyntaxException("Debe usar la etiqueta <instruccion> en"
								+ " vez de <"+child.getName()+">.");
					}
				}
			} else {
				throw new InvalidSyntaxException("La etiqueta <"+tree.getName()+"> no es valida."+
				" Debe usar <tam_ram>, <procesos> y <instrucciones>");
			}


		}	
	}

	private Proceso parseProceso(XMLElement tree) throws InvalidSyntaxException{
		Enumeration<XMLElement> f = tree.enumerateChildren();
		XMLElement nieto;
		String nombre = null;
		int color = -1;
		while(f.hasMoreElements()){
			nieto = f.nextElement();
			if(nieto.getName().toLowerCase().equals("nombre")){
				nombre = nieto.getContent();
			} else if (nieto.getName().toLowerCase().equals("color")){
				color = Integer.valueOf(nieto.getContent());
			} else {
				throw new InvalidSyntaxException("La etiqueta <"+nieto.getName()+"> es invalida.");
			}
		}
		if(nombre!=null && color !=-1){
			Proceso p = new Proceso(nombre, color);
			return p;
		} else {
			throw new InvalidSyntaxException("Debe especificar el <color> y <nombre> de cada proceso");
		}
	}

	private Instruccion parseInstruccion(XMLElement tree) throws InvalidSyntaxException{
		Enumeration<XMLElement> f = tree.enumerateChildren();
		XMLElement nieto;
		String proceso = null;
		char tipo=0;
		int dir_mem = -1;
		long tiempo = -1;

		while(f.hasMoreElements()){
			nieto = f.nextElement();
			if(nieto.getName().toLowerCase().equals("proceso")){
				proceso = nieto.getContent();
			} else if (nieto.getName().toLowerCase().equals("tipo")){
				tipo = nieto.getContent().toLowerCase().charAt(0);
				if(tipo!='r' && tipo!='w'){
					throw new InvalidSyntaxException("El tipo de las intrucciones es R o W, no '"+tipo+"'");
				}
			} else if (nieto.getName().toLowerCase().equals("dir_mem")){
				dir_mem = Integer.valueOf(nieto.getContent());
			} else if(nieto.getName().toLowerCase().equals("tiempo")){
				tiempo = Long.valueOf(nieto.getContent());
			} else {
				throw new InvalidSyntaxException("La etiqueta <"+nieto.getName()+"> es invalida.");
			}
		}
		if(proceso!=null && tipo!=0 && dir_mem!=-1 && tiempo!=-1){
			if(!procesos.containsKey(proceso)){
				throw new InvalidSyntaxException("No existe el proceso con nombre '"+proceso+"'");
			}
			Instruccion ins = new Instruccion(procesos.get(proceso), tipo, dir_mem, tiempo);
			return ins;
		} else {
			throw new InvalidSyntaxException("Debe especificar el <proceso> (su nombre), <tipo> (R o W) y <dir_mem> de cada instruccion");
		}
	}

}
